Skip to content

Conversation

@seojelee9999
Copy link
Contributor

@seojelee9999 seojelee9999 commented Dec 23, 2025

✨ 변경 사항


Cart crud기능 구현

✅ 테스트


  • 수동 테스트 완료
  • 테스트 코드 완료

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 전체 장바구니 시스템 구현: 메뉴 추가/수정, 항목 개별 삭제, 전체 장바구니 삭제 기능 제공
    • 장바구니 상품들의 총 가격 실시간 자동 계산
    • 수량이 0으로 설정될 경우 해당 항목 자동 제거
    • 빈 장바구니 자동 정리 기능

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

Walkthrough

장바구니 기능 전체 계층 구현 완료: 엔티티(Cart, CartItem), 저장소, DTO, 서비스, REST 컨트롤러 추가. User와 Cart 일대일 관계 설정 및 보안 유틸리티 추가. 새로운 에러 코드 정의.

Changes

코호트 / 파일 변경 사항
JPA 엔티티
src/main/java/com/campustable/be/domain/cart/entity/Cart.java, CartItem.java
Cart: User와 일대일 관계, CartItem 컬렉션 (CASCADE ALL, orphanRemoval true). CartItem: Menu/Cart와의 지연 로딩 관계, 수량 검증(@Min(0)). 편의 생성자 추가.
저장소 인터페이스
src/main/java/com/campustable/be/domain/cart/repository/CartRepository.java, CartItemRepository.java
CartRepository: findByUser(User) 파생 쿼리. CartItemRepository: findByCartAndMenu(), findByCart() 메서드 추가.
DTO
src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java, CartResponse.java, CartItemDto.java
CartRequest: menuId, quantity 필드. CartResponse: items, totalPrice, cartId. CartItemDto: CartItem 엔티티에서 DTO로 변환하는 생성자.
서비스 계층
src/main/java/com/campustable/be/domain/cart/service/CartService.java
updateCartItem: 메뉴/사용자 검증, 장바구니 생성/업데이트/삭제 로직. deleteCartItem: 소유권 검증, 아이템 제거 후 빈 장바구니 삭제. deleteCart, getCart 메서드.
REST 컨트롤러
src/main/java/com/campustable/be/domain/cart/controller/CartController.java, CartControllerDocs.java
CartController: POST /items (추가/업데이트), GET / (조회), DELETE /{cartId}, DELETE /items/{cartItemId} 엔드포인트. CartControllerDocs: Swagger/OpenAPI 문서화 인터페이스.
보안 및 글로벌 설정
src/main/java/com/campustable/be/global/common/SecurityUtil.java, User.java, ErrorCode.java
SecurityUtil: getCurrentUserId() 정적 메서드. User: cart 필드 추가 (일대일 관계). ErrorCode: CART_NOT_FOUND, CART_ITEM_NOT_FOUND 열거형 상수 추가.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Controller as CartController
    participant Service as CartService
    participant Repo as CartRepository<br/>CartItemRepository<br/>MenuRepository
    participant Security as SecurityUtil
    participant DB as Database

    rect rgb(240, 248, 255)
    Note over Client,DB: 장바구니 항목 추가/업데이트 흐름
    Client->>Controller: POST /api/cart/items<br/>(CartRequest: menuId, quantity)
    Controller->>Security: getCurrentUserId()
    Security->>DB: 인증 정보 확인
    Security-->>Controller: userId
    Controller->>Service: updateCartItem(menuId, quantity)
    Service->>Repo: MenuRepository.findById(menuId)
    Repo-->>Service: Menu
    Service->>Repo: CartRepository.findByUser(user)
    alt Cart 존재
        Repo-->>Service: Cart
        Service->>Repo: CartItemRepository.findByCartAndMenu()
        alt CartItem 존재
            Repo-->>Service: CartItem
            Service->>Service: quantity 업데이트<br/>(0이면 삭제)
        else CartItem 미존재
            Service->>Service: 새 CartItem 생성
            Service->>Repo: save(cartItem)
        end
    else Cart 미존재
        Service->>Service: 새 Cart 생성 (user)
        Service->>Repo: save(cart)
        Service->>Service: CartItem 생성
        Service->>Repo: save(cartItem)
    end
    Service->>Repo: 전체 CartItems 조회
    Repo-->>Service: List<CartItem>
    Service->>Service: 총액 계산
    Service-->>Controller: CartResponse(items, totalPrice, cartId)
    Controller-->>Client: 200 OK + CartResponse
    end

    rect rgb(240, 255, 240)
    Note over Client,DB: 장바구니 조회 흐름
    Client->>Controller: GET /api/cart
    Controller->>Security: getCurrentUserId()
    Security-->>Controller: userId
    Controller->>Service: getCart()
    Service->>Repo: CartRepository.findByUser(user)
    alt Cart 존재
        Repo-->>Service: Cart
        Service->>Service: CartItemDto 변환<br/>(Menu 정보 추출)
        Service->>Service: 총액 계산
        Service-->>Controller: CartResponse
    else Cart 미존재
        Service-->>Controller: 빈 CartResponse
    end
    Controller-->>Client: 200 OK + CartResponse
    end

    rect rgb(255, 240, 240)
    Note over Client,DB: 장바구니 항목 삭제 흐름
    Client->>Controller: DELETE /api/cart/items/{cartItemId}
    Controller->>Security: getCurrentUserId()
    Security-->>Controller: userId
    Controller->>Service: deleteCartItem(cartItemId)
    Service->>Repo: CartItemRepository.findById(cartItemId)
    Repo-->>Service: CartItem
    Service->>Service: 소유권 검증<br/>(Cart.user == userId)
    Service->>Repo: delete(cartItem)
    Service->>Repo: findByCart(cart)
    alt CartItems 존재
        Repo-->>Service: List<CartItem>
    else CartItems 미존재
        Service->>Repo: delete(cart)
    end
    Service-->>Controller: void
    Controller-->>Client: 204 No Content
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

변경 범위가 광범위하며 엔티티 관계 설정(일대일, 일대다, cascade, orphanRemoval), 트랜잭션 처리, 보안 검증, 예외 처리, 비즈니스 로직(장바구니 자동 삭제, 수량 0 처리 등)이 복합적으로 얽혀 있습니다. 다양한 계층(entity, repository, service, controller)의 이질적인 변경사항과 여러 참조 관계를 검토해야 합니다.

Possibly related PRs

Poem

당근을 담은 장바구니 🥕
Controller 부터 Database 까지,
사용자 맘껏 담고 빼고,
계산은 저절로 술술,
빈 바구니는 자동 정리!
어라, 정말 편리하군! 🐰✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목 'Cart crud기능 구현'은 풀 리퀘스트의 주요 변경 사항인 장바구니 CRUD 기능 구현을 명확하게 요약하고 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (10)
src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java (1)

7-11: 요청 DTO에 입력 검증 어노테이션 추가를 권장합니다.

menuId가 null이거나 quantity가 음수일 경우 예기치 않은 동작이 발생할 수 있습니다. Bean Validation 어노테이션을 추가하면 서비스 레이어 진입 전에 잘못된 입력을 걸러낼 수 있습니다.

🔎 검증 어노테이션 추가 제안
 package com.campustable.be.domain.cart.dto;
 
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
 import lombok.Getter;
 
 @Getter
 public class CartRequest {
 
+  @NotNull(message = "메뉴 ID는 필수입니다")
   private Long menuId;
+  @Min(value = 0, message = "수량은 0 이상이어야 합니다")
   private int quantity;
 }
src/main/java/com/campustable/be/domain/cart/entity/Cart.java (1)

38-40: 양방향 관계 동기화를 위한 편의 메서드 추가를 권장합니다.

생성자에서 user.setCart(this)를 호출하지 않으면, User 측에서 getCart()를 호출할 때 영속성 컨텍스트가 갱신되지 않아 불일치가 발생할 수 있습니다.

🔎 양방향 관계 동기화 제안
   public Cart(User user) {
     this.user = user;
+    user.setCart(this);
   }
src/main/java/com/campustable/be/global/common/SecurityUtil.java (1)

18-30: 에러 코드 의미론적 불일치

인증 정보가 없거나 유효하지 않은 경우 USER_NOT_FOUND를 사용하는 것은 의미상 맞지 않습니다. 이 경우는 "유저를 찾을 수 없음"이 아니라 "인증되지 않은 요청"에 해당합니다.

ACCESS_DENIED 또는 새로운 UNAUTHORIZED 에러 코드를 사용하는 것이 더 적절합니다. 이렇게 하면 클라이언트가 에러 원인을 정확히 파악하고 적절한 대응(재로그인 등)을 할 수 있습니다.

🔎 수정 제안
    if (authentication == null || authentication.getName() == null || authentication.getName().isBlank()) {
      log.error("SecurityUtil class에서 시큐리티 컨텍스트 홀더에 유저가 존재하지않아 에러발생.");
-      throw new CustomException(ErrorCode.USER_NOT_FOUND);
+      throw new CustomException(ErrorCode.ACCESS_DENIED);
    }

    try {
      return Long.parseLong(authentication.getName());
    } catch (NumberFormatException e) {
-      throw new CustomException(ErrorCode.USER_NOT_FOUND);
+      log.error("인증 정보의 사용자 ID 형식이 올바르지 않습니다: {}", authentication.getName());
+      throw new CustomException(ErrorCode.ACCESS_DENIED);
    }
src/main/java/com/campustable/be/domain/cart/controller/CartController.java (3)

30-34: 요청 DTO 유효성 검증 누락

CartRequest@Valid 어노테이션이 없습니다. Bean Validation을 사용하려면 @Valid를 추가해야 합니다.

🔎 수정 제안
+import jakarta.validation.Valid;
...
  @PostMapping("/items")
-  public ResponseEntity<CartResponse> addOrUpdateItems(@RequestBody CartRequest request) {
+  public ResponseEntity<CartResponse> addOrUpdateItems(@Valid @RequestBody CartRequest request) {

46-52: 삭제 API 응답 코드 개선 권장

void 반환 대신 ResponseEntity를 반환하여 적절한 HTTP 상태 코드(예: 204 No Content)를 명시하는 것이 RESTful API 설계에 더 적합합니다.

또한, URL 경로에 cartId를 받는 대신 현재 인증된 사용자의 장바구니를 자동으로 조회하는 방식이 더 안전합니다. 현재 구조에서는 서비스 레이어에서 소유권 검증이 반드시 수행되어야 합니다.

🔎 수정 제안
  @DeleteMapping("/{cartId}")
-  public void deleteCart(@PathVariable Long cartId) {
+  public ResponseEntity<Void> deleteCart(@PathVariable Long cartId) {
    cartService.deleteCart(cartId);
+    return ResponseEntity.noContent().build();
  }

또는 현재 사용자의 장바구니를 자동으로 조회하는 방식:

  @DeleteMapping("")
  public ResponseEntity<Void> deleteCart() {
    cartService.deleteCurrentUserCart();
    return ResponseEntity.noContent().build();
  }

서비스 레이어에서 deleteCart(Long cartId) 호출 시 현재 사용자가 해당 장바구니의 소유자인지 검증하는 로직이 있는지 확인이 필요합니다.


54-59: deleteCartItem도 동일하게 ResponseEntity 반환 권장

🔎 수정 제안
  @DeleteMapping("/items/{cartItemId}")
-  public void deleteCartItem(@PathVariable Long cartItemId) {
+  public ResponseEntity<Void> deleteCartItem(@PathVariable Long cartItemId) {
    cartService.deleteCartItem(cartItemId);
+    return ResponseEntity.noContent().build();
  }
src/main/java/com/campustable/be/domain/cart/entity/CartItem.java (2)

22-25: 생성자 위치를 필드 선언 이후로 이동하는 것을 고려하세요.

생성자가 필드 선언보다 먼저 정의되어 있습니다. 일반적인 Java 코딩 관례는 필드 선언, 생성자, 메서드 순서입니다. 가독성 향상을 위해 생성자를 필드 선언 이후로 이동하는 것을 권장합니다.


31-35: @joincolumn 애너테이션을 명시하여 외래 키 컬럼명을 명확히 하세요.

@ManyToOne 관계에 @JoinColumn 애너테이션이 누락되어 있습니다. JPA가 기본 컬럼명을 생성하지만, 명시적으로 지정하면 스키마가 더 명확해지고 유지보수가 용이해집니다.

🔎 제안하는 수정
 @ManyToOne(fetch = FetchType.LAZY)
+@JoinColumn(name = "menu_id")
 private Menu menu;

 @ManyToOne(fetch = FetchType.LAZY)
+@JoinColumn(name = "cart_id")
 private Cart cart;
src/main/java/com/campustable/be/domain/cart/service/CartService.java (2)

56-67: 조건문에 중괄호를 추가하여 가독성과 안전성을 높이세요.

Line 60의 else 문에 중괄호가 없습니다. 향후 코드 수정 시 의도하지 않은 버그가 발생할 수 있으므로, 단일 문장이더라도 중괄호를 사용하는 것이 좋습니다.

🔎 제안하는 수정
     if (cartItemOpt.isPresent()) {
       if (quantity == 0){
         cartItemRepository.delete(cartItemOpt.get());
       }
-      else cartItemOpt.get().setQuantity(quantity);
+      else {
+        cartItemOpt.get().setQuantity(quantity);
+      }
     } else {
       if (quantity > 0) {
         CartItem newItem = new CartItem(cart, menu);
         newItem.setQuantity(quantity);
         cartItemRepository.save(newItem);
       }
     }

71-86: 중복 코드를 헬퍼 메서드로 추출하세요.

CartItemDto 리스트 생성(lines 71-73)과 총 가격 계산(lines 84-86) 로직이 getCart() 메서드(lines 146-148, 150-152)에서도 동일하게 반복됩니다. 이를 private 헬퍼 메서드로 추출하면 코드 유지보수성이 향상됩니다.

🔎 제안하는 리팩토링

클래스에 다음 헬퍼 메서드들을 추가:

private List<CartItemDto> mapToCartItemDtos(Cart cart) {
  return cartItemRepository.findByCart(cart).stream()
      .map(CartItemDto::new)
      .toList();
}

private int calculateTotalPrice(List<CartItemDto> cartItems) {
  return cartItems.stream()
      .mapToInt(item -> item.getPrice() * item.getQuantity())
      .sum();
}

그런 다음 중복 코드를 다음과 같이 교체:

-    List<CartItemDto> cartItems = cartItemRepository.findByCart(cart).stream().
-        map(CartItemDto::new).
-        toList();
+    List<CartItemDto> cartItems = mapToCartItemDtos(cart);

     if (cartItems.isEmpty()) {
       // ...
     }

-    int totalPrice = cartItems.stream()
-        .mapToInt(item->item.getPrice() * item.getQuantity())
-        .sum();
+    int totalPrice = calculateTotalPrice(cartItems);

getCart() 메서드에도 동일하게 적용하세요.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7cca723 and dbafc4d.

📒 Files selected for processing (13)
  • src/main/java/com/campustable/be/domain/cart/controller/CartController.java
  • src/main/java/com/campustable/be/domain/cart/controller/CartControllerDocs.java
  • src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java
  • src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java
  • src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java
  • src/main/java/com/campustable/be/domain/cart/entity/Cart.java
  • src/main/java/com/campustable/be/domain/cart/entity/CartItem.java
  • src/main/java/com/campustable/be/domain/cart/repository/CartItemRepository.java
  • src/main/java/com/campustable/be/domain/cart/repository/CartRepository.java
  • src/main/java/com/campustable/be/domain/cart/service/CartService.java
  • src/main/java/com/campustable/be/domain/user/entity/User.java
  • src/main/java/com/campustable/be/global/common/SecurityUtil.java
  • src/main/java/com/campustable/be/global/exception/ErrorCode.java
🧰 Additional context used
🧬 Code graph analysis (5)
src/main/java/com/campustable/be/domain/cart/entity/Cart.java (5)
src/main/java/com/campustable/be/domain/cart/entity/CartItem.java (1)
  • Entity (16-43)
src/main/java/com/campustable/be/domain/user/entity/User.java (1)
  • Entity (13-49)
src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java (1)
  • Getter (6-11)
src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java (1)
  • Getter (8-24)
src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
  • Getter (8-15)
src/main/java/com/campustable/be/domain/cart/entity/CartItem.java (4)
src/main/java/com/campustable/be/domain/cart/entity/Cart.java (1)
  • Entity (20-43)
src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java (1)
  • Getter (6-11)
src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java (1)
  • Getter (8-24)
src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
  • Getter (8-15)
src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java (2)
src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java (1)
  • Getter (8-24)
src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
  • Getter (8-15)
src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java (1)
  • Getter (8-24)
src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java (2)
src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java (1)
  • Getter (6-11)
src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
  • Getter (8-15)
🔇 Additional comments (8)
src/main/java/com/campustable/be/domain/user/entity/User.java (1)

27-28: LGTM!

양방향 일대일 관계 설정이 올바르게 구현되었습니다. mappedBy로 Cart가 관계의 소유자임을 지정하고, CascadeType.ALLorphanRemoval = true로 User 삭제 시 Cart도 함께 정리되도록 적절하게 설정되었습니다.

src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)

8-15: LGTM!

Response DTO 구조가 적절합니다. @Builder 패턴을 사용하여 객체 생성이 깔끔하게 처리됩니다.

빈 장바구니의 경우 items가 null이 아닌 빈 리스트로 반환되는지 서비스 레이어에서 확인해 주세요.

src/main/java/com/campustable/be/domain/cart/entity/Cart.java (1)

31-33: User 삭제 시 orphan Cart 처리를 확인해 주세요.

User가 관계의 inverse side(mappedBy)이고 Cart가 owning side(@joincolumn)입니다. User 삭제 시 Cart가 orphan으로 남을 수 있으므로, 삭제 시나리오에서 Cart 정리가 올바르게 동작하는지 테스트가 필요합니다.

src/main/java/com/campustable/be/domain/cart/repository/CartRepository.java (1)

9-12: LGTM!

Spring Data JPA 레포지토리가 올바르게 구현되었습니다. findByUser 메서드는 Optional을 반환하여 null 안전성을 보장합니다.

참고: JpaRepository를 확장하는 인터페이스에서 @Repository 어노테이션은 선택적입니다 - Spring이 자동으로 감지합니다. 하지만 명시적으로 표기하는 것도 가독성 측면에서 괜찮습니다.

src/main/java/com/campustable/be/domain/cart/repository/CartItemRepository.java (1)

11-18: LGTM!

Spring Data JPA 쿼리 메서드가 올바르게 정의되었습니다. findByCartAndMenu는 장바구니에서 특정 메뉴 아이템을 찾을 때, findByCart는 장바구니의 전체 아이템 목록을 가져올 때 적절히 사용됩니다.

src/main/java/com/campustable/be/domain/cart/controller/CartControllerDocs.java (1)

10-88: 전반적으로 잘 작성된 API 문서입니다.

Swagger 문서가 상세하고 명확하게 작성되었습니다:

  • 모든 엔드포인트에 대한 요청/응답 형식 설명
  • 예외 상황 및 에러 코드 명시
  • 특수 동작(빈 장바구니 자동 삭제 등) 문서화
  • 보안 및 권한 검증 로직 설명

API 사용자가 쉽게 이해하고 사용할 수 있는 문서입니다.

src/main/java/com/campustable/be/domain/cart/entity/CartItem.java (1)

37-38: CartItem 엔터티에서 검증 시점을 명시하세요.

@Min 애너테이션은 기본적으로 JPA의 EntityManager 플러시 시점에 자동으로 검증됩니다(Hibernate Validator가 클래스패스에 있고 validation.mode가 AUTO일 때). 그러나 문제는 검증이 setQuantity 호출 시점이 아닌 persist/merge 시점에만 실행된다는 것입니다.

CartService에서 quantity를 설정한 후 유효하지 않은 데이터로 비즈니스 로직이 진행될 수 있습니다:

  • 서비스 메서드 매개변수에 @Valid 애너테이션을 추가하여 입력 단계에서 검증하거나
  • CartItem을 저장하기 전에 명시적으로 검증을 수행하세요

검증을 비즈니스 계층에서 수행하는 것이 데이터의 신뢰성을 높이고 런타임 오류를 방지하는 방법입니다.

src/main/java/com/campustable/be/domain/cart/service/CartService.java (1)

75-82: 양방향 @OnetoOne 관계에서 명시적 null 설정은 필수입니다.

Cart 삭제 시 cart.getUser().setCart(null) 호출(lines 76, 112, 129)은 선택 사항이 아니라 필수 패턴입니다. User.cart에 cascade = CascadeType.ALLorphanRemoval = true가 설정되어 있어도, 이들은 User가 cart 참조를 제거할 때만 작동합니다. CartService에서 직접 cart를 삭제할 때 메모리 상의 User 객체는 여전히 삭제된 Cart를 참조하게 되므로, 명시적으로 null을 설정하여 양방향 관계 일관성을 유지해야 합니다. 현재 구현은 올바른 방어적 코딩입니다.

Comment on lines +15 to +19
### 요청 파라미터
- `menu_id` (Long, required): 장바구니에 담을 메뉴 ID
- `quantity` (int, required): 담을 수량
- 1 이상: 해당 수량으로 장바구니에 추가 또는 수정
- 0: 해당 메뉴를 장바구니에서 삭제
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

API 문서의 파라미터 이름을 실제 JSON 필드명과 일치시키세요.

문서에서 menu_id (snake_case)로 표기되어 있지만, CartRequest DTO의 실제 필드명은 menuId (camelCase)입니다. Spring Boot의 기본 JSON 직렬화는 camelCase를 사용하므로, API 사용자는 menuId로 요청해야 합니다.

문서의 정확성을 위해 파라미터 이름을 menuId로 수정하세요.

🔎 제안하는 수정
         ### 요청 파라미터
-        - `menu_id` (Long, required): 장바구니에 담을 메뉴 ID
+        - `menuId` (Long, required): 장바구니에 담을 메뉴 ID
         - `quantity` (int, required): 담을 수량  
🤖 Prompt for AI Agents
In
src/main/java/com/campustable/be/domain/cart/controller/CartControllerDocs.java
around lines 15 to 19, the API doc uses the snake_case parameter name `menu_id`
which does not match the actual CartRequest DTO JSON field `menuId`; update the
documentation to use `menuId` (camelCase) and ensure any examples or
descriptions consistently reference `menuId` and its behavior (quantity rules
remain the same).

Comment on lines +17 to +23
public CartItemDto(CartItem entity) {
this.menuName = entity.getMenu().getMenuName();
this.price = entity.getMenu().getPrice();
this.quantity = entity.getQuantity();
this.menuUrl = entity.getMenu().getMenuUrl();
this.cartItemId = entity.getCartItemId();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "CartItem.java" -type f

Repository: CampusTable/campus-table-be

Length of output: 138


🏁 Script executed:

cat -n ./src/main/java/com/campustable/be/domain/cart/entity/CartItem.java

Repository: CampusTable/campus-table-be

Length of output: 1326


🏁 Script executed:

# Check for CartItem creation and service layer usage
rg -n "new CartItem\|CartItem\(" --type=java -A 3 | head -50

Repository: CampusTable/campus-table-be

Length of output: 53


🏁 Script executed:

# Search for CartItemDto and service methods that use it
rg -n "CartItemDto\|toDto" --type=java -B 2 -A 3 | head -80

Repository: CampusTable/campus-table-be

Length of output: 53


🏁 Script executed:

# Find CartItemDto class
find . -name "CartItemDto.java" -type f

Repository: CampusTable/campus-table-be

Length of output: 138


🏁 Script executed:

cat -n ./src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java

Repository: CampusTable/campus-table-be

Length of output: 836


🏁 Script executed:

# Check if there's a ForeignKey constraint or database schema
find . -name "*.sql" -o -name "*schema*" -type f | head -20

Repository: CampusTable/campus-table-be

Length of output: 53


🏁 Script executed:

# Search for all references to CartItemDto instantiation
rg -n "new CartItemDto\|CartItemDto(" --type=java -B 3 -A 2

Repository: CampusTable/campus-table-be

Length of output: 179


🏁 Script executed:

# Search for CartItemDto instantiation
rg -n "CartItemDto" --type=java -B 3 -A 2

Repository: CampusTable/campus-table-be

Length of output: 4399


🏁 Script executed:

cat -n ./src/main/java/com/campustable/be/domain/cart/service/CartService.java | head -80

Repository: CampusTable/campus-table-be

Length of output: 3456


🏁 Script executed:

# Check if there are any FK constraints or database validation
rg -n "ForeignKey|nullable|unique|JoinColumn" --type=java | grep -i "cart\|menu"

Repository: CampusTable/campus-table-be

Length of output: 956


🏁 Script executed:

rg -n "@JoinColumn\|@ManyToOne" --type=java -A 1 | grep -A 1 "CartItem"

Repository: CampusTable/campus-table-be

Length of output: 53


🏁 Script executed:

rg -n "menu" --type=java ./src/main/java/com/campustable/be/domain/cart/entity/CartItem.java

Repository: CampusTable/campus-table-be

Length of output: 216


🏁 Script executed:

# Check if there are any other places where CartItem is created
rg -n "new CartItem" --type=java -B 3 -A 3

Repository: CampusTable/campus-table-be

Length of output: 814


@JoinColumn(nullable = false) 추가를 고려하세요.

CartItem의 menu 필드에 명시적인 nullable = false 제약이 없습니다. 현재 코드는 CartService에서 검증된 Menu로만 CartItem을 생성하고 있으나, JPA 수준에서 null 방지를 보장하지 않습니다. 다음과 같이 수정하여 데이터베이스 레벨의 무결성을 강화할 수 있습니다:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(nullable = false)
private Menu menu;

현재 CartItemDto 생성은 트랜잭션 내에서 이루어지므로 LazyInitializationException은 발생하지 않습니다.

🤖 Prompt for AI Agents
In src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java around
lines 17 to 23: the review recommends enforcing non-nullability at the JPA level
for the CartItem.menu association; update the CartItem entity (where the menu
field is declared) to add @JoinColumn(nullable = false) alongside
@ManyToOne(fetch = FetchType.LAZY) so the DB schema enforces NOT NULL for the
foreign key; after changing the annotation, run/update your schema migration (or
allow JPA to update DDL) to apply the constraint.

Comment on lines +68 to +71
//Cart
CART_NOT_FOUND(HttpStatus.NOT_FOUND, "장바구니를 찾을수 없습니다."),

CART_ITEM_NOT_FOUND(HttpStatus.NOT_FOUND, "장바구니 개별목록을 찾을수 없습니다.");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

한국어 띄어쓰기 수정 필요

에러 메시지에서 "찾을수"는 "찾을 수"로 띄어쓰기가 필요합니다 (의존명사 앞은 띄어씀).

🔎 수정 제안
  //Cart
-  CART_NOT_FOUND(HttpStatus.NOT_FOUND, "장바구니를 찾을수 없습니다."),
+  CART_NOT_FOUND(HttpStatus.NOT_FOUND, "장바구니를 찾을 수 없습니다."),

-  CART_ITEM_NOT_FOUND(HttpStatus.NOT_FOUND, "장바구니 개별목록을 찾을수 없습니다.");
+  CART_ITEM_NOT_FOUND(HttpStatus.NOT_FOUND, "장바구니 개별 목록을 찾을 수 없습니다.");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//Cart
CART_NOT_FOUND(HttpStatus.NOT_FOUND, "장바구니를 찾을수 없습니다."),
CART_ITEM_NOT_FOUND(HttpStatus.NOT_FOUND, "장바구니 개별목록을 찾을수 없습니다.");
//Cart
CART_NOT_FOUND(HttpStatus.NOT_FOUND, "장바구니를 찾을 수 없습니다."),
CART_ITEM_NOT_FOUND(HttpStatus.NOT_FOUND, "장바구니 개별 목록을 찾을 수 없습니다.");
🤖 Prompt for AI Agents
In src/main/java/com/campustable/be/global/exception/ErrorCode.java around lines
68 to 71, the Korean error messages use incorrect spacing ("찾을수" → should be "찾을
수"). Update the two enum entries CART_NOT_FOUND and CART_ITEM_NOT_FOUND to use
"찾을 수" instead of "찾을수" so the messages read "장바구니를 찾을 수 없습니다." and "장바구니 개별목록을
찾을 수 없습니다." respectively.

@seojelee9999 seojelee9999 merged commit d53f2f9 into main Dec 23, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants